package club.xyes.zkh.ecommerce.wechat.service.impl;

import club.xyes.zkh.ecommerce.commons.exception.InternalServerErrorException;
import club.xyes.zkh.ecommerce.wechat.dto.*;
import club.xyes.zkh.ecommerce.wechat.service.WeChatService;
import club.xyes.zkh.ecommerce.wechat.util.HttpUtil;
import club.xyes.zkh.ecommerce.wechat.util.crypt.WxBizDataCrypt;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Create by 郭文梁 2019/4/17 0017 15:24
 * WeChatServiceImpl
 * 微信相关API实现
 *
 * @author 郭文梁
 * @data 2019/4/17 0017
 */
@Service
@Slf4j
public class WeChatServiceImpl implements WeChatService {
    private static final String ACCESS_TOKEN_ERR_CODE_KEY = "errcode";
    private static final String SEND_TEMPLATE_MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=%s";
    private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    private static final String CODE_2_SESSION_API_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
    private static final String UNIFORM_MESSAGE_API_URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=%s";
    private static final String OAUTH2_TOKEN_API_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
    private final RestTemplate restTemplate;
    private Map<String, WxAccessToken> localAccessTokenCache = new ConcurrentHashMap<>();

    @Autowired
    public WeChatServiceImpl(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public Code2SessionResult code2Session(String appid, String secret, String code) {
        String apiUrl = String.format(CODE_2_SESSION_API_URL, appid, secret, code);
        Code2SessionResult result = restTemplate.getForObject(apiUrl, Code2SessionResult.class);
        if (result == null || result.getOpenid() == null) {
            throw new InternalServerErrorException("Failure to parse wechat server returned data:" + result);
        }
        return result;
    }

    @Override
    public WxUserInfoResult toUserInfo(String sessionKey, String iv, String data) {
        String resultJson = new WxBizDataCrypt(sessionKey).decode(iv, data);
        return JSON.parseObject(resultJson, WxUserInfoResult.class);
    }

    @Override
    public WxAccessToken getAccessToken(String appId, String secret) throws IOException {
        WxAccessToken lastToken = localAccessTokenCache.get(appId);
        if (lastToken != null && !lastToken.expired()) {
            return lastToken;
        }
        String tokenUrl = String.format(ACCESS_TOKEN_URL, appId, secret);
        String response = HttpUtil.get(tokenUrl);
        JSONObject jsonRes = JSON.parseObject(response);
        if (jsonRes.get(ACCESS_TOKEN_ERR_CODE_KEY) == null) {
            lastToken = jsonRes.toJavaObject(WxAccessToken.class);
            lastToken.setRefreshTime(System.currentTimeMillis() / 1000);
            localAccessTokenCache.put(appId, lastToken);
        } else {
            throw new InterruptedIOException("Unknown response json:" + response);
        }
        return lastToken;
    }

    @Override
    public SendTemplateMsgResult sendTemplateMsg(String appId,
                                                 WxAccessToken token,
                                                 String formId,
                                                 String templateId,
                                                 String toUserOpenId,
                                                 Map<String, TemplateMsgData> data,
                                                 Map<String, String> miniProgram,
                                                 Map<String, Object> options) throws IOException {
        miniProgram = miniProgram == null ? new HashMap<>(1) : miniProgram;
        Map<String, Object> postBody = options == null ? new HashMap<>(10) : options;
        postBody.put("touser", toUserOpenId);
        postBody.put("template_id", templateId);
        postBody.put("form_id", formId);
        miniProgram.put("appid", appId);
        postBody.put("miniprogram", miniProgram);
        postBody.put("data", data);
        String postJson = JSON.toJSONString(postBody);
        String url = String.format(SEND_TEMPLATE_MSG_URL, token.getAccessToken());
        String response = HttpUtil.postJson(url, postJson);
        return JSON.parseObject(response, SendTemplateMsgResult.class);
    }

    @Override
    public UniformMessageSendResult sendUniformMessage(String appId,
                                                       String mpAppId,
                                                       WxAccessToken token,
                                                       String toUserOpenId,
                                                       String templateId,
                                                       String url,
                                                       Map<String, TemplateMsgData> data) throws IOException {
        String apiUrl = String.format(UNIFORM_MESSAGE_API_URL, token.getAccessToken());
        Map<String, String> miniprogram = new HashMap<>(2);
        miniprogram.put("appid", mpAppId);
        miniprogram.put("pagepath", "index");

        Map<String, Object> mpTemplateMsg = new HashMap<>(5);
        mpTemplateMsg.put("appid", appId);
        mpTemplateMsg.put("template_id", templateId);
        mpTemplateMsg.put("url", url);
        mpTemplateMsg.put("miniprogram", miniprogram);
        mpTemplateMsg.put("data", data);

        Map<String, Object> postData = new HashMap<>(2);
        postData.put("touser", toUserOpenId);
        postData.put("mp_template_msg", mpTemplateMsg);
        String postBodyJson = JSON.toJSONString(postData);

        log.debug("SendUniformMessage Post[{}]", postBodyJson);
        String resp = HttpUtil.postJson(apiUrl, postBodyJson);
        log.debug("SendUniformMessage Response[{}]", resp);
        return JSON.parseObject(resp, UniformMessageSendResult.class);
    }

    @Override
    public WxOauth2AccessTokenResult oauth2CodeToken(String appId, String secret, String code) throws IOException {
        String url = String.format(OAUTH2_TOKEN_API_URL, appId, secret, code);
        String resp = HttpUtil.get(url);
        WxOauth2AccessTokenResult result = JSON.parseObject(resp, WxOauth2AccessTokenResult.class);
        if (!result.isSuccess()) {
            log.debug("Could not get access token: resp[{}]", resp);
        }
        return result;
    }
}
